<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <script>
            // 1.封装数据代理
            function Vue({ data }) {
                // 先将数据放到vm._data上
                this._data = data;
                // 将数据遍历出来，在vm上代理一份
                for (let key in this._data) {
                    // 给实例对象vm扩展一些属性，并设置自存取器属性
                    // Object.defineProperty()第一个参数是需要定义的对象，第二个参数是对象的属性值，第三个参数是配置对象
                    Object.defineProperty(this, key, {
                        get: function proxyGetter() {
                            // 当值被读取时，返回vm_data上的值
                            return this._data[key];
                        },
                        set: function proxySetter(newVal) {
                            // 当值被修改时，去修改vm._data上的值
                            this._data[key] = newVal;
                        },
                    });
                    observer(this._data);
                }
5
                // 数据劫持
                // 封装一个专门的函数，来做数据劫持
                function observer(data) {
                    // 判断数据是否为null或不是对象，只有是对象才继续做深度劫持
                    if (typeof data !== "object" || data === null) return;
                    new Observer(data);
                }

                // 封装一个Observer类（创建发布者--数据劫持）
                class Observer {
                    constructor(data) {
                        this.data = data;
                        // 判断当前需要被劫持的值是数组还是对象，然后分发到不同的方法中
                        if (Array.isArray(this.data)) {
                            this.observerArray(this.data);
                        } else {
                            this.walk(this.data);
                        }
                    }
                    // 如果是数组格式的数据劫持
                    observerArray(data) {
                        data.forEach((item) => {
                            observer(item);
                        });
                    }
                    // 如果是对象格式的数据劫持
                    walk(data) {
                        for (let key in data) {
                            defineReactive(data, key);
                        }
                    }
                }

                // 封装数据劫持，将vm._data的属性重写，加上存取器属性
                function defineReactive(data, key) {
                    let value = data[key];
                    observer(value);
                    Object.defineProperty(data, key, {
                        get: function reactiveGetter() {
                            // 读取值时返回this._data原来的值
                            return value;
                        },
                        set: function reactiveSetter(newVal) {
                            //判断如果新值和旧值一样,则不做任何操作
                            if (newVal === value) return;
                            // 将新值赋给value，以便后续更新视图
                            value = newVal;
                            console.log("通知所有的使用当前数据的组件 进行更新");
                        },
                    });
                }
            }

            const vm = new Vue({
                data: {
                    count: 1,
                    score: {
                        math: 80,
                    },
                },
            });
            console.log(vm);
        </script>
    </body>
</html>
